﻿using FileZillaConfig.Types;
using System;
using System.Text;
using System.Xml.Linq;

namespace FileZillaConfig.ConfigClasses
{
    public class ServerSettings
    {
        private PortsList _serverPorts;
        private int _numberOfThreads = 3;
        private int _maxUsers = 1;
        private int _connectionTimeout = 120;
        private int _noTransferTimeout = 600;
        private int _loginTimeout = 60;
        private string _welcomeMessage;
        private bool _hideWelcomeMessage = false;
        private string _IPBindings = "*";
        private IPFilter _IPFilter;
        private Passive _passive;
        private Security _security;
        private Admin _admin;
        private Logging _logging;
        private bool _enableGSS = false;
        private bool _promptGSS = false;
        private MODEZCompression _modez;
        private Autoban _autoban;
        private SSL _ssl;
        private Misc _misc;
        private SpeedLimits _speedLimits;

        #region PROPERTIES
        /// <summary>
        /// Speed limits settings
        /// </summary>
        public SpeedLimits SpeedLimits
        {
            get { return _speedLimits; }
        }


        /// <summary>
        /// Miscellaneous settings
        /// </summary>
        public Misc Miscellaneous
        {
            get { return _misc; }
        }


        /// <summary>
        /// SSL/TLS settings
        /// </summary>
        public SSL SSL
        {
            get { return _ssl; }
        }


        /// <summary>
        /// Autoban settings
        /// </summary>
        public Autoban Autoban
        {
            get { return _autoban; }
        }


        /// <summary>
        /// File transfer compression settings
        /// </summary>
        public MODEZCompression ModeZCompression
        {
            get { return _modez; }
        }


        /// <summary>
        /// Prompt for password if necessary (GSS)
        /// </summary>
        public bool GSSPromptPwd
        {
            get { return _promptGSS; }
            set { _promptGSS = value; }
        }


        /// <summary>
        /// Enable Kerberos GSS support
        /// </summary>
        public bool GSSKerberosEnable
        {
            get { return _enableGSS; }
            set { _enableGSS = value; }
        }


        /// <summary>
        /// Logging settings
        /// </summary>
        public Logging Logging
        {
            get { return _logging; }
        }


        /// <summary>
        /// Admin interface settings
        /// </summary>
        public Admin Admin
        {
            get { return _admin; }
        }


        /// <summary>
        /// Security settings
        /// </summary>
        public Security Security
        {
            get { return _security; }
        }


        /// <summary>
        /// Passive settings
        /// </summary>
        public Passive Passive
        {
            get { return _passive; }
        }


        /// <summary>
        /// IPs allowed or not to connect to the server
        /// </summary>
        public IPFilter IPFilter
        {
            get { return _IPFilter; }
        }

        /// <summary>
        /// Listen on these ports
        /// </summary>
        public PortsList ServerPorts
        {
            get
            {
                return this._serverPorts;
            }
        }

        /// <summary>
        /// This value should be a multiple of the number of processors installed on your system.
        /// </summary>
        public int NumberOfThreads
        {
            get
            {
                return this._numberOfThreads;
            }
            set
            {
                this._numberOfThreads = value;
            }
        }

        /// <summary>
        /// Max number of users (0 for unlimited users)
        /// </summary>
        public int MaxUsers
        {
            get
            {
                return this._maxUsers;
            }
            set
            {
                this._maxUsers = value;
            }
        }

        /// <summary>
        /// Connections timeout in seconds (1-9999, 0 for no timeout) 
        /// </summary>
        public int ConnectionTimeout
        {
            get
            {
                return this._connectionTimeout;
            }
            set
            {
                if (value >= 0 && value <= 9999)
                {
                    this._connectionTimeout = value;
                }
                else
                {
                    throw new System.Exception("Value must be integer between 0 and 9999.");
                }
            }
        }

        /// <summary>
        /// No transfer timeout in seconds (600 - 9999, 0 for no timeout)
        /// </summary>
        public int NoTransferTimeout
        {
            get
            {
                return this._noTransferTimeout;
            }
            set
            {
                if ((value >= 600 && value <= 9999) || value == 0)
                {
                    this._noTransferTimeout = value;
                }
                else
                {
                    throw new System.Exception("Value must be integer between 600 and 9999 or 0.");
                }
            }
        }

        /// <summary>
        /// Login timeout in seconds (1-9999, 0 for no timeout)
        /// </summary>
        public int LoginTimeout
        {
            get
            {
                return this._loginTimeout;
            }
            set
            {
                if (value >= 0 && value <= 9999)
                {
                    this._loginTimeout = value;
                }
                else
                {
                    throw new System.Exception("Value must be integer between 0 and 9999.");
                }
            }
        }

        /// <summary>
        /// Only 75 characters per line. Additional characters will be removed
        /// </summary>
        public string WelcomeMessage
        {
            get
            {
                return this._welcomeMessage;
            }
            set
            {
                StringBuilder sB = new StringBuilder();

                string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                foreach (string line in lines)
                {
                    sB.AppendLine((line.Length > 75)?line.Substring(0, 75):line);
                }

                this._welcomeMessage = sB.ToString();
            }
        }

        /// <summary>
        /// Hide welcome message in log
        /// </summary>
        public bool HideWelcomeMessage
        {
            get
            {
                return this._hideWelcomeMessage;
            }
            set
            {
                this._hideWelcomeMessage = value;
            }
        }

        /// <summary>
        /// Bind the server to the following IP adresse (* - bind to all adresses)
        /// </summary>
        public string IPBindings
        {
            get
            {
                return this._IPBindings;
            }
            set
            {
                this._IPBindings = (value == "") ? "*" : value;
            }
        } 
        #endregion

        public ServerSettings()
        {
            //Construct classes
            this._serverPorts = new PortsList();
            this._IPFilter = new IPFilter();
            this._passive = new Passive();
            this._security = new Security();
            this._admin = new Admin();
            this._logging = new Logging();
            this._modez = new MODEZCompression();
            this._autoban = new Autoban();
            this._ssl = new SSL();
            this._misc = new Misc();
            this._speedLimits = new SpeedLimits();
        }

        public XElement getXML()
        {
            XElement Settings = new XElement("Settings");

            if (this.ServerPorts.List.Count == 0) this.ServerPorts.addPort(21);

            Settings.Add(newItem("Serverports", this.ServerPorts.portString));
            Settings.Add(newItem("Number of Threads", this._numberOfThreads));
            Settings.Add(newItem("Maximum user count", this._maxUsers));
            Settings.Add(newItem("Timeout", this._connectionTimeout));
            Settings.Add(newItem("No Transfer Timeout", this._noTransferTimeout));
            Settings.Add(newItem("Login Timeout", this._loginTimeout));
            Settings.Add(newItem("Initial Welcome Message", this._welcomeMessage));
            Settings.Add(newItem("Hide Welcome Message", this._hideWelcomeMessage));
            Settings.Add(newItem("IP Bindings", this._IPBindings));
            Settings.Add(newItem("IP Filter Allowed", this._IPFilter.Allowed));
            Settings.Add(newItem("IP Filter Disallowed", this._IPFilter.Disallowed));
            Settings.Add(newItem("Use custom PASV ports", this._passive.UseCustomPortRange));
            Settings.Add(newItem("Custom PASV min port", this._passive.getPortRange()["min"]));
            Settings.Add(newItem("Custom PASV max port", this._passive.getPortRange()["max"]));
            Settings.Add(newItem("Custom PASV IP", this._passive.CustomIP));
            Settings.Add(newItem("Custom PASV IP type", this._passive.CustomIPMode));
            Settings.Add(newItem("Custom PASV IP server", this._passive.ExternalIPServer));
            Settings.Add(newItem("No External IP On Local", this._passive.NoExternalIPOnLocal));
            Settings.Add(newItem("Allow Incoming FXP", this._security.AllowIncoming));
            Settings.Add(newItem("Allow outgoing FXP", this._security.AllowOutgoing));
            Settings.Add(newItem("No Strict In FXP", this._security.StrictIPFilterIncoming));
            Settings.Add(newItem("No Strict Out FXP", this._security.StrictIPFilterOutgoing));
            Settings.Add(newItem("Show Pass in Log", this._misc.ShowPassInLog));
            Settings.Add(newItem("Allow shared write", this._misc.AllowSharedWrite));
            Settings.Add(newItem("Active ignore local", this._misc.IgnoreUnroutableIP));
            Settings.Add(newItem("Buffer Size", this._misc.InternalBufferSize));
            Settings.Add(newItem("Network Buffer Size", this._misc.SocketBufferSize));
            Settings.Add(newItem("Admin port", this._admin.Port));
            Settings.Add(newItem("Admin IP Bindings", this._admin.IPBindings));
            Settings.Add(newItem("Admin IP Addresses", this._admin.IPAdresses));
            Settings.Add(newItem("Admin Password", this._admin.Password.Get));
            Settings.Add(newItem("Enable logging", this._logging.Enabled));
            Settings.Add(newItem("Logsize limit", this._logging.SizeLimit));
            Settings.Add(newItem("Logfile type", this._logging.FileType));
            Settings.Add(newItem("Logfile delete time", this._logging.DeleteOldFilesAfter));
            Settings.Add(newItem("Use GSS Support", this._enableGSS));
            Settings.Add(newItem("GSS Prompt for Password", this._promptGSS));
            Settings.Add(newItem("Mode Z Use", this._modez.Enabled));
            Settings.Add(newItem("Mode Z min level", this._modez.MinimumCompression));
            Settings.Add(newItem("Mode Z max level", this._modez.MaximumCompression));
            Settings.Add(newItem("Mode Z allow local", this._modez.AllowLocal));
            Settings.Add(newItem("Mode Z disallowed IPs", this._modez.DisallowedIPs));
            Settings.Add(newItem("Enable SSL", this._ssl.Enabled));
            Settings.Add(newItem("Allow explicit SSL", this._ssl.AllowExplicitFTP));
            Settings.Add(newItem("SSL Key file", this._ssl.SSLKeyFile));
            Settings.Add(newItem("SSL Certificate file", this._ssl.SSLCertFile));
            Settings.Add(newItem("Implicit SSL ports", this._ssl.ImplicitSSLPorts.portString));
            Settings.Add(newItem("Force explicit SSL", this._ssl.DisallowUnencrypted));
            Settings.Add(newItem("SSL Key Password", this._ssl.KeyPassword));
            Settings.Add(newItem("Force PROT P", this._ssl.ForcePROTPEncryption));
            Settings.Add(newItem("Autoban enable", this._autoban.Enabled));
            Settings.Add(newItem("Autoban attempts", this._autoban.Attempts));
            Settings.Add(newItem("Autoban type", this._autoban.type));
            Settings.Add(newItem("Autoban time", this._autoban.Time));
            Settings.Add(newItem("Download Speedlimit Type", this._speedLimits.DownloadType));
            Settings.Add(newItem("Upload Speedlimit Type", this._speedLimits.UploadType));
            Settings.Add(newItem("Download Speedlimit", this._speedLimits.DownloadLimit));
            Settings.Add(newItem("Upload Speedlimit", this._speedLimits.UploadLimit));

            Settings.Add(this._speedLimits.getXML());


            return Settings;
        }

        private XElement newItem(string name, bool value)
        {
            string type = "numeric";
            string val = Convert.ToInt16(value).ToString();

            return getItemElement(val, type, name);
        }

        private XElement newItem(string name, int value)
        {
            string type = "numeric";
            string val = value.ToString();

            return getItemElement(val, type, name);
        }

        private XElement newItem(string name, string value)
        {
            string type = "string";
            string val = value;

            return getItemElement(val, type, name);
        }

        private XElement newItem(string name, Enum value)
        {
            string type = "numeric";
            string val = Convert.ToInt16(value).ToString();

            return getItemElement(val, type, name);
        }


        private XElement getItemElement(string val, string type, string name)
        {
            return new XElement("Item", val,
                        new XAttribute("name", name),
                         new XAttribute("type", type));
        }

        

 
    }

}
